package com.project.srtp.gateway.config;

import com.project.srtp.gateway.filter.JwtTokenProvider;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.context.ServerSecurityContextRepository;
import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 曹行阳
 * @version 0.1
 * @date 2025/6/15 15:42
 */
@Configuration
@RequiredArgsConstructor
public class GatewaySecurityConfig {

    private final JwtTokenProvider jwtTokenProvider;


    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
        http
                .csrf().disable()
                .cors().and()
                .authorizeExchange()
                .pathMatchers("/api/user/login", "/api/user/register","/api/secure/reinit","/api/keys/reinit", "/api/secure/init","/api/keys/storeInitKey","/swagger-ui/**", "/v3/api-docs/**").permitAll()
                .anyExchange().authenticated()
                .and()
                .authenticationManager(authenticationManager())
                .securityContextRepository(securityContextRepository());

        return http.build();
    }

    @Bean
    public ReactiveAuthenticationManager authenticationManager() {
        return authentication -> {
            String token = (String) authentication.getCredentials();
            if (token != null && jwtTokenProvider.validateToken(token)) {
                String username = jwtTokenProvider.getUsernameFromToken(token);
                List<SimpleGrantedAuthority> authorities = jwtTokenProvider.getRolesFromToken(token)
                        .stream()
                        .map(SimpleGrantedAuthority::new)
                        .collect(Collectors.toList());

                AbstractAuthenticationToken auth = new AbstractAuthenticationToken(authorities) {
                    @Override
                    public Object getCredentials() {
                        return token;
                    }

                    @Override
                    public Object getPrincipal() {
                        return username;
                    }
                };
                auth.setAuthenticated(true);
                return Mono.just(auth);
            }
            return Mono.empty();
        };
    }

    @Bean
    public ServerSecurityContextRepository securityContextRepository() {
        return new ServerSecurityContextRepository() {
            private final WebSessionServerSecurityContextRepository delegate = new WebSessionServerSecurityContextRepository();


            @Override
            public Mono<Void> save(ServerWebExchange exchange, SecurityContext context) {
                return delegate.save(exchange, context);
            }

            @Override
            public Mono<SecurityContext> load(ServerWebExchange exchange) {
                String authHeader = exchange.getRequest().getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
                if (authHeader != null && authHeader.startsWith("Bearer ")) {
                    String authToken = authHeader.substring(7);
                    Authentication auth = new AbstractAuthenticationToken(null) {
                        @Override
                        public Object getCredentials() {
                            return authToken;
                        }

                        @Override
                        public Object getPrincipal() {
                            return null;
                        }
                    };
                    auth.setAuthenticated(false);
                    return authenticationManager().authenticate(auth)
                            .map(authentication -> new SecurityContext() {
                                @Override
                                public Authentication getAuthentication() {
                                    return authentication;
                                }

                                @Override
                                public void setAuthentication(Authentication authentication) {
                                    throw new UnsupportedOperationException("Not supported");
                                }
                            });
                }
                return Mono.empty();
            }
        };
    }

    @Bean
    public CorsWebFilter corsWebFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedOrigin("*");
        config.addAllowedMethod("*");
        config.addAllowedHeader("*");

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return new CorsWebFilter(source);
    }
}
